﻿//
// <copyright file="ApplicationEnvironment.cs" company="Microsoft">
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>
//
using System;
using Microsoft.ServiceHosting.ServiceRuntime;
using System.Configuration;
using System.Web.Configuration;
using System.IO;


namespace Microsoft.Samples.ServiceHosting.HelloFabric
{

    public enum ApplicationModel
    {
        Unknown,
        Web,
        Desktop,
        Cloud
    }

    public class Settings
    {
        internal Settings() { }
        public string this[string name]
        {
            get
            {
                ApplicationEnvironment.DetectEnvironment();
                switch (ApplicationEnvironment.ApplicationModel)
                {
                    case ApplicationModel.Cloud:
                        try
                        {
                            return RoleManager.GetConfigurationSetting(name);
                        }
                        catch { return null; }
                    case ApplicationModel.Desktop:
                        return ConfigurationManager.AppSettings[name];
                    case ApplicationModel.Web:
                        return WebConfigurationManager.AppSettings[name];
                    default:
                        throw new ApplicationException("Impossible");
                }
            }
        }
    }

    public class LocalStores
    {   
        string storesDir;

        internal LocalStores()
        {
            ApplicationEnvironment.DetectEnvironment();
            if (ApplicationEnvironment.ApplicationModel != ApplicationModel.Cloud)
            {
                this.storesDir =
                  Path.Combine(Path.Combine(Path.GetTempPath(), "localstores"),
                               Path.GetRandomFileName());
            }
        }
       
       public string this[string name]
        { 
            get
            {
                ApplicationEnvironment.DetectEnvironment();
                switch (ApplicationEnvironment.ApplicationModel)
                {
                    case ApplicationModel.Cloud:
                      return RoleManager.GetLocalResource(name).RootPath;
                    case ApplicationModel.Desktop: 
                    case ApplicationModel.Web:
                      string dirPath = Path.Combine(storesDir,name);
                      Directory.CreateDirectory(dirPath);
                      return dirPath;
                    default:
                        throw new ApplicationException("Impossible");
                }
            }
        }
    }
    
    static public class ApplicationEnvironment
    {
        // Useful when debugging some code executed without a debugger attached (like online) // TODO: If it gets big, it can slow down the system ?!?
        static public readonly System.Text.StringBuilder Logs = new System.Text.StringBuilder();
        static public readonly string LogsFileName = DateTime.UtcNow.ToString("yyyy'-'MM'-'dd'_'HH'-'mm'-'ss'-'fffffff") + ".txt";
        static public bool IgnoreVerboseLogs = true;

        public static void FlushLogs()
        {
            try
            {
                var stream = new MemoryStream(System.Text.Encoding.UTF8.GetBytes(Logs.ToString()));
                BlogEngine.Core.Providers.BlogService.SaveFileToStorage(stream, "logs", LogsFileName);
            }
            catch { }
        }

        public static object LogErrorAndThrowException(Exception ex, string format, params object[] args)
        {
            return LogErrorAndThrowException<object>(ex, format, args);
        }

        public static T LogErrorAndThrowException<T>(Exception ex, string format, params object[] args)
        {
            var msg = string.Format(format, args);
            if (System.Diagnostics.Debugger.IsAttached) // TODO: Also useful without debugger => Add config setting (which is true by default)?
                msg += Environment.NewLine + ex;

            LogError(msg);
            throw new Exception(msg, ex);
        }


        static public ApplicationModel ApplicationModel
        {
            get;
            private set;
        }

        static public LocalStores LocalStores
        {
            get;
            private set;
        }

        static public Settings Settings
        {
            get;
            private set;
        }

        static public void LogAlert(string msg) 
        {
            DetectEnvironment();
            logger.LogAlert(msg); 
        }

        static public void LogError(string msg)
        {
            DetectEnvironment();
            logger.LogError(msg);
        }

        static public void LogWarning(string msg)
        {
            DetectEnvironment();
            logger.LogWarning(msg); 
        }

        static public void LogInformation(string msg)
        {
            DetectEnvironment();
            logger.LogInformation(msg); 
        }

        static public void LogVerbose(string msg)
        {
            DetectEnvironment();
            logger.LogVerbose(msg);
        }

        #region Useful Overloads

        static private string FormatMessage(string format, Object[] args)
        {
            return String.Format(format, args);
        }

        static public void LogAlert(string format, params Object[] args)
        {
            LogAlert(FormatMessage(format, args));
        }

        static public void LogError(string format,params Object[] args)
        {
            LogError(FormatMessage(format, args));
        }

        static public void LogWarning(string format,params Object[] args)
        {
            LogWarning(FormatMessage(format, args));
        }

        static public void LogInformation(string format,params Object[] args)
        {
            LogInformation(FormatMessage(format, args));
        }

        static public void LogVerbose(string format,params Object[] args)
        {
            LogVerbose(FormatMessage(format, args));
        }

        #endregion

        #region Implementation

        private static LogPrimitives logger;

        internal abstract class LogPrimitives
        {
            public abstract void LogAlert(string msg);
            public abstract void LogError(string msg);
            public abstract void LogWarning(string msg);
            public abstract void LogInformation(string msg);
            public abstract void LogVerbose(string msg);

            protected static string FormatAndAppendLog(string tag, string message)
            {
                var log = string.Format("{0} - {1,02} {2,12}: {3}",
                                        DateTime.UtcNow.ToString("yyyy'-'MM'-'dd' 'HH':'mm':'ss'.'fff"),
                                        System.Threading.Thread.CurrentThread.ManagedThreadId,
                                        tag, message);

                if (IgnoreVerboseLogs && tag == "Verbose")
                    return log;

                Logs.AppendLine(log);
                //File.WriteAllText(Path.Combine(System.Web.HttpRuntime.AppDomainAppPath, "logs.txt"), Logs.ToString()); // TODO: Use when the debugger doesn't even start => Create a config setting (which is true by default)?
                return log;
            }

            protected static void LogMessage(string tag, string message)
            {
                var log = FormatAndAppendLog(tag, message);
                System.Diagnostics.Debug.WriteLine(log);
            }
        }

        class RoleManagerLogger : LogPrimitives
        {
            public override void LogAlert(string msg)
            {
                LogMessage("Critical", msg);
                RoleManager.WriteToLog("Critical", msg);
            }

            public override void LogError(string msg)
            {
                LogMessage("Error", msg);
                RoleManager.WriteToLog("Error", msg);
            }

            public override void LogWarning(string msg)
            {
                LogMessage("Warning", msg);
                RoleManager.WriteToLog("Warning", msg);
            }

            public override void LogInformation(string msg)
            {
                LogMessage("Information", msg);
                RoleManager.WriteToLog("Information", msg);
            }

            public override void LogVerbose(string msg)
            {
                LogMessage("Verbose", msg);
                RoleManager.WriteToLog("Verbose", msg);
            }
        }

        class ConsoleLogger : LogPrimitives
        {
            static ConsoleColor defaultColor = System.Console.ForegroundColor;

            private static void WriteMessage(ConsoleColor color, string tag, string message)
            {
                var log = FormatAndAppendLog(tag, message);
                System.Console.ForegroundColor = color;
                System.Console.WriteLine(log);
            }

            public override void LogAlert(string msg)
            {
                WriteMessage(ConsoleColor.Magenta, "Alert", msg);
            }

            public override void LogError(string msg)
            {
                WriteMessage(ConsoleColor.Red, "Error", msg);
            }

            public override void LogWarning(string msg)
            {
                WriteMessage(ConsoleColor.Yellow, "Warning", msg);
            }

            public override void LogInformation(string msg)
            {
                WriteMessage(ConsoleColor.White, "Information", msg);
            }

            public override void LogVerbose(string msg)
            {
                WriteMessage(defaultColor, "Verbose", msg);
            }
        }

        class TraceLogger : LogPrimitives
        {   
            private static void WriteMessage(string tag, string message)
            {
                var log = FormatAndAppendLog(tag, message);
                System.Diagnostics.Trace.WriteLine(log);
            }

            public override void LogAlert(string msg)
            {
                WriteMessage("Alert", msg);
                System.Diagnostics.Debugger.Break();
            }

            public override void LogError(string msg)
            {
                WriteMessage("Error", msg);
            }

            public override void LogWarning(string msg)
            {
                WriteMessage("Warning", msg);
            }

            public override void LogInformation(string msg)
            {
                WriteMessage("Information", msg);
            }

            public override void LogVerbose(string msg)
            {
                WriteMessage("Verbose", msg);
            }
        }

        static ApplicationEnvironment()
        {
            DetectEnvironment();
            ApplicationEnvironment.Settings = new Settings();
            ApplicationEnvironment.LocalStores = new LocalStores();
        }

        public static void DetectEnvironment() // TODO: This is a workaround; for some reason, in the DevFabric, IsRoleManagerRunning = false while starting (so it is configured as Web) // TODO: Doesn't happen anymore...
        {
            if (RoleManager.IsRoleManagerRunning)
            {
                ApplicationEnvironment.ApplicationModel = ApplicationModel.Cloud;
                ApplicationEnvironment.logger = new RoleManagerLogger();
            }
            else if (System.Web.HttpContext.Current != null)
            {
                ApplicationEnvironment.ApplicationModel = ApplicationModel.Web;
                ApplicationEnvironment.logger = new TraceLogger();
                System.Diagnostics.Trace.Listeners.Add(new System.Web.WebPageTraceListener());
            }
            else
            {
                ApplicationEnvironment.ApplicationModel = ApplicationModel.Desktop;
                ApplicationEnvironment.logger = new ConsoleLogger();
            }
        }

        #endregion
    }    
}
